home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / infoserv / gopher / Unix / gopher-gateways / techinfo / techinpher / v1.0 / datastruct.c.Z / datastruct.c
Encoding:
C/C++ Source or Header  |  1993-02-23  |  19.6 KB  |  842 lines

  1. /*
  2.   This software is copyrighted by the University of Pennsylvania.
  3.   Read COPYRIGHT for details.
  4.   */
  5.  
  6. #include <stdio.h>
  7. #include <sys/types.h>
  8. #include <string.h>
  9. #include <sys/time.h>
  10. #include <sys/wait.h>
  11. #include <sys/resource.h>
  12. #include <signal.h>
  13. #include <sys/dir.h>
  14. #include <sys/stat.h>
  15.  
  16. #include "pdb.h"
  17. #include "node.h"
  18. #include "messages.h"
  19. #include "network.h"
  20.  
  21. #include "gophernodes.h"
  22.  
  23. extern char     *transfields[];
  24. extern int      num_transfields;
  25. extern char     trans_type;
  26. extern char     *msglist[]; /* the list of error messages */
  27. extern char     RESVNODES_FILE[];
  28. extern char     GW_NODES_FILE[];
  29. extern char     GW_NODES_DIR[];
  30. extern char     GW_NODES_BAK_FILE[];
  31. extern unsigned long _allocated;
  32. extern int      strlower(char *str);
  33. extern void     catch_child();
  34. extern int      debug;
  35. extern short    todaysdate;
  36. extern int      next_id;
  37.  
  38. #define PRIME1   5003
  39. #define PRIME2   1009
  40.   
  41. static struct s1      *a1[PRIME1];   /* gateway info table */
  42. static struct s2      *a2[PRIME2];   /* nodeid table */ 
  43. static struct resvnode reservednodes[MAX_RESV_NODES];
  44. static int            numresvnodes = 0;
  45. static int            numgophnodes = 0;
  46. /* static int            time_lastchecked = 0; */
  47. static int            day_lastrmnodes = 0;
  48.  
  49. int                   tables_changed = 0;
  50. int                   lastused_changed = 0;
  51. int                   addednewnodes = 0;
  52. int                   savedatastruct_pid = 0;
  53.  
  54. struct resvnode *get_resvnode(long node)
  55. {
  56.   int idx, foundr;
  57.  
  58.   for (idx = 0, foundr = 0; !foundr && idx < numresvnodes; ) {
  59.     if (reservednodes[idx].nodeid == node)
  60.       foundr = 1;
  61.     else
  62.       idx++;
  63.   }
  64.       
  65.   if (foundr)
  66.     return ( &(reservednodes[idx]) );
  67.   else
  68.     return ((struct resvnode *) NULL);
  69. }
  70.  
  71.  
  72.  
  73.  
  74. extern struct s1 *getnode_bynodeid (long nodeid)
  75. {
  76.   struct s2 *nlist;
  77.   int found;
  78.   int h;
  79.  
  80.   h =  nodeid % PRIME2;
  81.   /* NOT assuming that the lists in a2 are sorted; though it'd be faster */
  82.   for (found=0, nlist = a2[h]; !found && nlist != NULL;) {
  83.     if (nodeid == nlist->nodeid)
  84.       found = 1;
  85.     else
  86.       nlist = nlist->nextnode;
  87.   }
  88.   
  89.   if (nlist == (struct s2 *) NULL) {
  90.     return (struct s1 *)NULL;
  91.   }
  92.   else {
  93.     return (nlist->node);
  94.   }
  95. }
  96.  
  97.  
  98.  
  99.  
  100. static int
  101.   ghash(char *gophName, char *gophHost, char *gophPort, char *gophPath)
  102. {
  103.   int sum, i, j;
  104.   
  105.   strlower (gophHost);
  106.  
  107.   sum = 0;
  108.   for ( i=0; gophHost[i] ; i++)
  109.     sum += gophHost[i];
  110.  
  111.   for ( i=0; gophPath[i]; i++)
  112.     sum += gophPath[i];
  113.  
  114.   for ( i=0; gophName[i]; i++)
  115.     sum += gophName[i];
  116.  
  117.   return (((sum + atoi(gophPort)) * 15511) % PRIME1);
  118. }
  119.  
  120.  
  121.  
  122. extern struct s1 *getnode_bygopherinfo (char **gopherfields)
  123. {
  124.   int h;
  125.   struct s1 *temp;
  126.   int found;
  127.   char gophtype;
  128.   char *gophpath, *gophserver, *gophport, *gophtitle;
  129.  
  130.   gophtype = *(gopherfields[0]);
  131.   gophtitle = gopherfields[0] + 1;
  132.   gophpath = gopherfields[1];
  133.   gophserver = gopherfields[2];
  134.   gophport = gopherfields[3];
  135.  
  136.   found = 0;
  137.   h = ghash(gophtitle, gophserver, gophport, gophpath);
  138.   
  139.   /*
  140.     For now, assuming that the lists in a1 are NOT sorted.
  141.     To be more efficient, we should make the list be sorted. 
  142.   */
  143.  
  144.   for (temp = a1[h]; !found && temp != NULL; )
  145.     {
  146.       if (strcmp(temp->gopherport, gophport) == 0 &&
  147.       strcmp(temp->gopherserver,gophserver) == 0 &&
  148.       strcmp(temp->gopherpath, gophpath) == 0 &&
  149.       strcmp(temp->gophertitle, gophtitle) == 0 &&
  150.       temp->gophertype == gophtype) {
  151.     found = 1;
  152.       }
  153.       else
  154.     temp = temp->nextnode;
  155.     }
  156.   
  157.   if (found)
  158.     return (temp);
  159.   else
  160.     return (struct s1 *)NULL;
  161. }
  162.  
  163.  
  164. void reservednodes_unload (void)
  165. {
  166.   int n;
  167.  
  168.   if (debug)
  169.     fprintf (stderr, "Freeing %d reserved nodes\n", numresvnodes);
  170.  
  171.   for (n = 0; n < numresvnodes; n++) {
  172.     do_free (reservednodes[n].title);
  173.     do_free (reservednodes[n].file);
  174.     
  175.     if (reservednodes[n].children)
  176.       free (reservednodes[n].children);
  177.     reservednodes[n].numchildren = 0;
  178.   }
  179.   numresvnodes = 0;
  180. }
  181.  
  182.  
  183. /*
  184.   MODIFIED make_link_block code from MIT (it had a bug) --lam
  185.   */
  186.  
  187. static long * make_link_block (char links[], short *linkcnt, int chknodeids)
  188. {
  189.   char      *cp;
  190.   int       numlink;
  191.   long      *nid_block;
  192.   int       chadded;
  193.   int       c, c2;
  194.   long      nid;
  195.   short     repeat;
  196.   short     nodexists;       
  197.   struct    s1 *tempnode;
  198.   
  199.   numlink = *links ? strcnt(links, ',') + 1 : 0;
  200.   nid_block = (long *) domalloc((unsigned int) numlink * sizeof(long));
  201.   
  202.   cp = links;
  203.   chadded = 0;
  204.   for (c = 0; c < numlink; c++) {
  205.     nid = atol(cp);
  206.  
  207.     /* if nodeid is not a reserved node and is also not in a1[] table,
  208.        then don't add it */
  209.  
  210.     if (is_reserved_node (nid))
  211.       nodexists = 1;
  212.     else {
  213.       if (getnode_bynodeid(nid) != (struct s1 *) NULL)
  214.     nodexists = 1;
  215.       else
  216.     nodexists = 0;
  217.     }
  218.  
  219.     /* no repeats allowed */
  220.     for (repeat = 0, c2 = 0; nodexists && c2 < c && !repeat; c2++)
  221.       if (nid_block[c2] == nid)
  222.     repeat++;
  223.     
  224.     if (!repeat && nodexists) {
  225.       nid_block[chadded] = nid;
  226.       chadded++;
  227.     }
  228.     /* MIT's BUG: if (repeat) continue -- but it doesn't increment cp */
  229.     cp = index(cp, ',') + 1;
  230.   }
  231.   *linkcnt = chadded;
  232.   /* chadded is better than numlink because of repeats --lam */
  233.   return nid_block;
  234. }
  235.  
  236.  
  237.  
  238.  
  239. int reservednodes_load(int unloadfirst)
  240. {
  241.   char *resvfields[NUMFIELDS_RESVNODES];
  242.   FILE *resvfile;
  243.   char line[NLINE_MAXLEN];
  244.   int numlines = 0;
  245.   int numf;
  246.   
  247.   resvfile = fopen (RESVNODES_FILE, "r");
  248.   
  249.   if (resvfile == NULL) {
  250.     perror (RESVNODES_FILE);
  251.     return 0;
  252.   }
  253.   
  254.   if (unloadfirst)
  255.     reservednodes_unload();
  256.   
  257.   fprintf (stderr, "Reading reserved nodes from %s...", RESVNODES_FILE);
  258.   
  259.   numresvnodes = 0;
  260.   while (fgets (line, NLINE_MAXLEN, resvfile) != NULL) {
  261.     if (*line == '#')
  262.       continue;
  263.     numlines++;
  264.     
  265.     parsefields (line, resvfields, &numf, DLM, NUMFIELDS_RESVNODES);
  266.     if (numf >= NUMFIELDS_RESVNODES) {
  267.       reservednodes[numresvnodes].nodeid = atoi(resvfields[0]);
  268.       
  269.       reservednodes[numresvnodes].title =
  270.     (char *) domalloc (strlen(resvfields[1]) + 1);
  271.       strcpy (reservednodes[numresvnodes].title, resvfields[1]);
  272.       
  273.       reservednodes[numresvnodes].file =
  274.     (char *) domalloc (strlen(resvfields[2]) + 1);
  275.       strcpy (reservednodes[numresvnodes].file, resvfields[2]);
  276.       
  277.       reservednodes[numresvnodes].children = make_link_block
  278.     (resvfields[3], &(reservednodes[numresvnodes].numchildren), 1);
  279.     }
  280.     else
  281.       fprintf (stderr, "Not enough fields in line %d\n",numlines);
  282.     numresvnodes++;
  283.   }
  284.   
  285.   fclose (resvfile); /* close file */
  286.   fprintf (stderr, "Done.\nRead %d lines, %d reserved nodes. _allocated=%ld bytes\n", numlines,numresvnodes, _allocated);
  287.  
  288.   return numresvnodes;
  289. }
  290.  
  291.  
  292.  
  293. /* add node to array 1.  */
  294. static void a1_add (struct s1 *nd)
  295. {
  296.   int h;
  297.   
  298.   h = ghash (nd->gophertitle, nd->gopherserver, nd->gopherport, nd->gopherpath);
  299.   nd->nextnode = a1[h];
  300.   /* add nd to the beginning of the list */
  301.   a1[h] = nd;
  302.   return;
  303. }
  304.  
  305.  
  306.  
  307.  
  308. static void a2_add (struct s1 *nd)
  309. {
  310.   struct s2 *nd2;
  311.   int h;
  312.  
  313.   /* add new node to the beginning of the list */
  314.  
  315.   nd2 = (struct s2 *) domalloc (sizeof (struct s2));
  316.   nd2->node = nd;
  317.   nd2->nodeid  = nd->nodeid;
  318.  
  319.   h = nd->nodeid % PRIME2;  /* which list does it belong on? */
  320.   nd2->nextnode = a2[h];
  321.   a2[h] = nd2;
  322.  
  323.   return;
  324. }
  325.  
  326.  
  327.  
  328.  
  329. /* allocate memory for node and put initial values in it */
  330. /* If checkexists && the nodeid or gopherinfo exists already,
  331.    NULL is returned */
  332.  
  333. struct s1 *
  334. init_gopher_node (char **gofields, long nodeid, short lastused, int checkexists)
  335. {
  336.   struct s1 *nd;
  337.   int found;
  338.   
  339.   if (checkexists) { /*serv,port,path */ /*type-title path server port */
  340.     nd = getnode_bygopherinfo(gofields);
  341.     
  342.     if (nd != NULL) {
  343.       fprintf (stderr, "Gopher info exists (nodeid %d)\n", nodeid);
  344.       return NULL;
  345.     }
  346.     nd = getnode_bynodeid (nodeid);
  347.     
  348.     if (nd != NULL) {
  349.       return NULL;
  350.     }
  351.   }
  352.   
  353.   nd = (struct s1 *) domalloc (sizeof(struct s1));
  354.   nd->nodeid         = nodeid;
  355.   nd->lastused       = lastused;
  356.   nd->gophertype     = *(gofields[0]);
  357.   nd->gophertitle    = (char *)domalloc(strlen(gofields[0])+1);
  358.   nd->gopherpath     = (char *)domalloc(strlen(gofields[1])+1);
  359.   nd->gopherserver   = (char *)domalloc(strlen(gofields[2])+1);
  360.   nd->gopherport     = (char *)domalloc(strlen(gofields[3])+1);
  361.   
  362.   strcpy (nd->gophertitle,    gofields[0]+1);
  363.   strcpy (nd->gopherpath,     gofields[1]);
  364.   strcpy (nd->gopherserver,   gofields[2]);
  365.   strcpy (nd->gopherport,     gofields[3]);
  366.  
  367.   a1_add(nd);
  368.   a2_add(nd);
  369.  
  370.   return nd;
  371. }
  372.  
  373.  
  374.  
  375.  
  376.  
  377. struct s1 *load_gopher_node (char **gophernodestr)
  378. {
  379.   long nodeid;
  380.   short lastused;
  381.   char *gofields[NUMFIELDS_GOPHER];
  382.   int numf;
  383.  
  384.   nodeid = atoi(gophernodestr[0]);
  385.   lastused = atoi(gophernodestr[1]);
  386.  
  387.   if (is_reserved_node(nodeid)) {
  388.     fprintf (stderr, "load_gopher_node: %d is reserved nodeid\n", nodeid);
  389.     return (struct s1 *) NULL;
  390.   }
  391.  
  392.   else if (GW_NODE_UNUSED(lastused)) {
  393.     fprintf (stderr, "load_gopher_node: Nodeid %d last used %d, too old.\n",
  394.          nodeid, lastused);
  395.     return (struct s1 *) NULL;
  396.   }
  397.  
  398.   else {
  399.     parsefields (gophernodestr[2],gofields,&numf,GOPHER_DLM,NUMFIELDS_GOPHER);
  400.     if (numf < NUMFIELDS_GOPHER) { /* gopher info not parseable */
  401.       fprintf (stderr, "Only %d Gopher fields in %s\n",
  402.            numf, gophernodestr[2]);
  403.       return (struct s1 *) NULL;
  404.     }
  405.     else
  406.       return(init_gopher_node(gofields, nodeid, lastused, 1));
  407.   }
  408. }
  409.  
  410.  
  411.  
  412. void gophernodes_unload ()
  413. {
  414.   int i, numunloaded;
  415.   struct s1 *temp1, *temp2;
  416.   struct s2 *temp3, *temp4;
  417.  
  418.   for (i = 0, numunloaded = 0; i < PRIME1; i++) {
  419.     for (temp1 = a1[i]; temp1 != NULL; temp1 = temp2 ) {
  420.       temp2 = temp1->nextnode;
  421.  
  422.       /* free the char data as well... */
  423.       do_free(temp1->gophertitle);
  424.       do_free(temp1->gopherserver);
  425.       do_free(temp1->gopherpath);
  426.       do_free(temp1->gopherport);
  427.  
  428.       free (temp1);
  429.       numunloaded++;
  430.     }
  431.   }
  432.  
  433.   if (debug)
  434.     fprintf (stderr, "Freed %d gopher nodes\n", numunloaded);
  435.  
  436.   for (i = 0; i < PRIME2; i++) {
  437.     for (temp3 = a2[i]; temp3 != NULL; ) {
  438.       temp4 = temp3->nextnode;
  439.       free (temp3); /* there's no other data to worry about here */
  440.       temp3 = temp4;
  441.     } 
  442.   }
  443.  
  444.   numgophnodes = 0;
  445. }
  446.  
  447.  
  448.  
  449. int gophernodes_load(int unloadfirst)
  450. {
  451. #define ADAY 86400
  452.  
  453.   int numf;
  454.   char *gonodefields[NUMFIELDS_GOPHNODES];
  455.   int numlines = 0;
  456.   FILE *gophernodesfile;
  457.   char line[NLINE_MAXLEN];
  458.  
  459.   gophernodesfile = fopen(GW_NODES_FILE, "r");
  460.   if (gophernodesfile == NULL) {
  461.     perror (GW_NODES_FILE);
  462.     return 0;
  463.   }
  464.   
  465.   if (unloadfirst) 
  466.     gophernodes_unload ();
  467.   init_arrays();
  468.  
  469.   fprintf (stderr, "Reading gopher nodes from %s...", GW_NODES_FILE);
  470.   addednewnodes = 0;
  471.   tables_changed = 0;
  472.   lastused_changed = 0;
  473.  
  474.   todaysdate = time(0) / ADAY;
  475.   numgophnodes = 0;
  476.   while (fgets(line, NLINE_MAXLEN, gophernodesfile) != NULL) {
  477.     if (*line == '#')
  478.       continue;
  479.     numlines++;
  480.  
  481.     parsefields (line, gonodefields, &numf, DLM, NUMFIELDS_GOPHNODES);
  482.     if (numf >= NUMFIELDS_GOPHNODES) {
  483.       if (load_gopher_node (gonodefields))
  484.     numgophnodes++;
  485.     }
  486.     else
  487.       fprintf (stderr, "Not enough fields in line %d\n", numlines);
  488.   }
  489.  
  490.   fclose (gophernodesfile);
  491.   fprintf (stderr, "Done.\nRead %d lines, %d gopher nodes.  _allocated=%ld\n", numlines, numgophnodes, _allocated);
  492.  
  493.   return numgophnodes;
  494. }    
  495.  
  496.  
  497.  
  498.  
  499.  
  500. /* Two arrays of pointers to nodes.  Make them all null to start with */
  501. init_arrays ()
  502. {
  503.   long i;
  504.  
  505.   for (i = 0; i < PRIME1; i++)
  506.     a1[i] = (struct s1 *) NULL;
  507.  
  508.   for (i = 0; i < PRIME2; i++)
  509.     a2[i] = (struct s2 *) NULL;
  510. }
  511.  
  512.  
  513. /* Load reserved nodes table, gopher nodes tables */
  514. /* maybe add option to load one or the other & not both from disk */
  515. int datastruct_load (int unloadfirst)
  516. {
  517.   int okay;
  518.  
  519.   okay = gophernodes_load(unloadfirst);
  520.   /*
  521.     get gopher nodes loaded first so that when reserved nodes are loaded,
  522.     no unknown gopher nodes will be added to the reserved table
  523.     */
  524.  
  525.   if (okay)
  526.     okay = reservednodes_load(unloadfirst);
  527.  
  528.   get_nextid();
  529.   fprintf (stderr, "Nextid = %d\n", next_id);
  530.   return okay;
  531. }
  532.  
  533. void cleancache(void)  /* any files whose mtime is older than cache timeout
  534.               should be removed */
  535. {
  536.   DIR *cachedir;
  537.   struct direct *nextfile;
  538.   struct stat stbuf;
  539.   char filename[MAXPATHLEN];
  540.   long now;
  541.   int numrm;
  542.  
  543. /*  fprintf (stderr, "Removing old files from %s...", CACHEDIR); */
  544.   cachedir = opendir(CACHEDIR);
  545.   
  546.   if (cachedir == NULL)  /* cache directory is not being used */
  547.     return;
  548.  
  549.   now = time (0);
  550.   numrm = 0;
  551.   while ( (nextfile = readdir(cachedir)) != NULL ) {
  552.     if (strcmp (".", nextfile->d_name) != 0 &&
  553.     strcmp ("..", nextfile->d_name) != 0) {
  554.       sprintf (filename, "%s%s", CACHEDIR, nextfile->d_name);
  555.       if (stat(filename, &stbuf) >= 0 
  556.       && (now - stbuf.st_mtime) > CACHETIMEOUT) {
  557.     unlink (filename);
  558.     numrm++;
  559.       }
  560.     }
  561.   }
  562.   (int) closedir(cachedir);
  563.   if (numrm)
  564.     fprintf (stderr, "T=%d. Removed %d files from the cache\n", time(0),numrm);
  565. }
  566.  
  567.  
  568. /* Safekeeping:
  569.  
  570.   If an unreserved node _is_ a child of a reserved 
  571.   node, then it is safe, even if it hasn't been used
  572.   for a while.
  573.  
  574.   */
  575.  
  576. static void get_safekeeping (long **safe, int *numsafe)
  577. {
  578.   int r, c;
  579.   int maxsafe;
  580.  
  581.   /* first, we figure out the max # of nodes that could be safe */
  582.   for (r = 0, maxsafe = 0; r < numresvnodes; r++)
  583.     maxsafe += reservednodes[r].numchildren;
  584.  
  585.   *safe = (long *) malloc ( (unsigned int)maxsafe * sizeof(long));
  586.   *numsafe = 0;
  587.  
  588.   for (r = 0, maxsafe = 0; r < numresvnodes; r++) {
  589.     for (c = 0; c < reservednodes[r].numchildren; c++) {
  590.       if ( ! is_reserved_node(reservednodes[r].children[c] ) ) {
  591.     (*safe)[*numsafe] = reservednodes[r].children[c];
  592.     (*numsafe)++;
  593.       }
  594.     }
  595.   }
  596. }
  597.  
  598.  
  599.  
  600. /* a2 is the nodeid table.
  601.    Using the given nodeid, find the corresponding struct s2
  602.    in the nodeid table and remove it from the table
  603.    */
  604. static void  removenode_nodeidtab (long nodeid)
  605. {
  606.   int h, done;
  607.   struct s2 *nd, *b4, *tmp;
  608.  
  609.   h = nodeid % PRIME2;
  610.  
  611.   for (done = 0, b4 = NULL, nd = a2[h]; !done && nd != NULL; ) {
  612.     if (nodeid == nd->nodeid) {
  613.       done = 1;
  614.       if (b4 == NULL)
  615.     a2[h] = nd->nextnode; /* nd was the first on the list */
  616.       else
  617.     b4->nextnode = nd->nextnode;
  618.  
  619.       tmp = nd;
  620.       nd = nd->nextnode;
  621.       free (tmp);              /* get rid of nodeid node */
  622.     }
  623.     else {
  624.       b4 = nd;
  625.       nd = nd->nextnode;
  626.     }
  627.   }
  628. }
  629.  
  630.  
  631. /* remove unused should be improved so that it checks to see if
  632.    todaysdate has changed since the last time nodes were removed
  633.    from the tables because the granularity is a day; it's more 
  634.    efficient to avoid checking thousands of nodes unnecessarily */
  635.  
  636. static void remove_unused(void)
  637. {
  638.   int n;
  639.   struct s1 *node, *b4, *temp;
  640.   long *safelist;
  641.   int numsafe;
  642.   int s, is_safe;
  643.   sigset_t prev_sigmask;
  644.   sigset_t new_sigmask;
  645.   sigset_t temp_sigmask;
  646.   int numremoved = 0;
  647.  
  648.   if (todaysdate == day_lastrmnodes) {
  649.     return;
  650.   }
  651.   else
  652.     fprintf (stderr, "checking remove_unused (%d != %d)\n", todaysdate, day_lastrmnodes);
  653.  
  654.   day_lastrmnodes = todaysdate;
  655.  
  656.   /* block Children from interrupting this delicate stuff */
  657.   sigprocmask (SIG_SETMASK, (sigset_t *)NULL, &prev_sigmask);
  658.   new_sigmask = prev_sigmask | SIGCHLD;
  659.   sigprocmask (SIG_SETMASK, &new_sigmask, NULL);
  660.   /*  sigprocmask (SIG_SETMASK, NULL, &temp_sigmask);
  661.       fprintf (stderr, "Signal mask has been set to %d.\n", temp_sigmask); */
  662.  
  663.   get_safekeeping(&safelist, &numsafe);
  664.   for (n = 0; n < PRIME1; n++) {
  665.     for (node = a1[n], b4=NULL; node != (struct s1 *) NULL; ) 
  666.       { 
  667.     if ( ! GW_NODE_UNUSED(node->lastused) ) {
  668.       b4 = node;
  669.       node = node->nextnode;
  670.     }
  671.     else {  
  672.       for (s = 0, is_safe = 0; s < numsafe && !is_safe; s++)
  673.         if (safelist[s] == node->nodeid) is_safe = 1;
  674.       
  675.       if ( is_safe ) {
  676.         b4 = node;
  677.         node = node->nextnode;
  678.       }
  679.       else {         /* node should be removed from the tables  */
  680.         fprintf (stderr, "rm %d (%d)\n",
  681.              node->nodeid, node->lastused);
  682.         numremoved++;
  683.         removenode_nodeidtab (node->nodeid);
  684.         if (b4 == NULL)  /* node was the first on the list */
  685.           a1[n] = node->nextnode;
  686.         else
  687.           b4->nextnode = node->nextnode;
  688.         /* leave b4 as it is... */
  689.         temp = node;
  690.         node = node->nextnode;
  691.         free(temp);       /* Get rid of old node */
  692.       }
  693.     }
  694.       }
  695.   }
  696.   
  697.   if (safelist)
  698.     free (safelist);
  699.  
  700.   tables_changed += numremoved;
  701.  
  702.   /* okay, set the signal mask back to whatever it was */
  703.   sigprocmask (SIG_SETMASK, &prev_sigmask, NULL);
  704.   if (numremoved)
  705.     fprintf (stderr, "Removed %d nodes from tables\n", numremoved);
  706. }
  707.  
  708.  
  709. static void save_gwnodes (char *tempfilename)
  710. {
  711.   FILE *tempfile;
  712.   int n;
  713.   int g = 0;
  714.   struct s1 *node;
  715.   
  716.   tempfile = fopen (tempfilename, "w");
  717.   if (tempfile == NULL) { /* we're in trouble... */
  718.     fprintf (stderr, "Unable to write gopher nodes temp file\n");
  719.     perror (tempfilename);
  720.   }
  721.   else {
  722.     fprintf (stderr, "%d: Saving gateway nodes to tempfile %s\n",
  723.          getpid(), tempfilename);
  724.     
  725.     for (n = 0; n < PRIME1; n++) {
  726.       for (node = a1[n]; node != (struct s1 *) NULL; node=node->nextnode)
  727.     {/* go down the list writing out the data to temp file */
  728.       
  729.       /*<nodeid>:<lastu>:<cnt>:<inicnt>:<gtype><gtitle>TAB<gpath>TAB<gserver>TAB<gport>*/
  730.       fprintf(tempfile, "%ld:%d:%c%s\t%s\t%s\t%s\n",
  731.           node->nodeid, node->lastused,
  732.           node->gophertype, node->gophertitle, node->gopherpath,
  733.           node->gopherserver, node->gopherport);
  734.       g++;
  735.     }
  736.       fflush(tempfile);
  737.     }
  738.     fclose (tempfile);
  739.     
  740.     /* rename() requires the two files to be on the same file system */
  741.     rename(GW_NODES_FILE, GW_NODES_BAK_FILE);
  742.     rename(tempfilename, GW_NODES_FILE);
  743.     chmod(GW_NODES_FILE,0644);
  744.     fprintf(stderr, "%d: wrote %d gopher nodes to disk\n", getpid(), g);
  745.   }
  746. }
  747.  
  748.  
  749.  
  750. /* write gophernodes arrays out to disk */
  751. void save_datastruct ()
  752. {
  753.   char *tempfilename;
  754.   int forkstat;
  755.   union wait status;
  756.   struct rusage rusage;
  757.  
  758.   (void) cleancache();     /* parent cleans the cache */
  759.  
  760.   remove_unused();  /* get rid of useless nodeids in the tables */
  761.  
  762.   if (! tables_changed && ! addednewnodes && ! lastused_changed) {
  763.     return;
  764.   }
  765.   
  766.   get_nextid();
  767.   fprintf (stderr,
  768.        "T=%d. save_datastruct: added %d newnodes, %d lastused changes, %d table changes, nextid = %d.\n", 
  769.        time(0), addednewnodes, lastused_changed, tables_changed, next_id);
  770.   
  771.   /* Because these variables need to be known by the parent,
  772.      the child cannot update them */
  773.   tables_changed = 0;
  774.   addednewnodes = 0;
  775.   lastused_changed = 0;
  776.  
  777.   /* generate temporary file name */
  778.   tempfilename = tempnam(GW_NODES_DIR, "gwnodes");
  779.   forkstat = fork();
  780.   if (forkstat != 0)
  781.     signal(SIGCHLD, catch_child);
  782.   
  783.   if (forkstat <= 0) { /* child or error */
  784.     save_gwnodes (tempfilename);
  785.     if (forkstat == 0) {  /* child */
  786.       exit (0);
  787.     }
  788.   }
  789.   else { /* parent */
  790.     savedatastruct_pid = forkstat;
  791.     fprintf (stderr, "Save_datastruct child is %d.\n", forkstat);
  792. /*    forkstat = wait3(&status, WNOHANG, &rusage); */
  793.     do_free(tempfilename);  /* tempnam called malloc() */
  794.   }
  795. }
  796.  
  797.  
  798.  
  799.  
  800.  
  801. void catch_hup(void)
  802. {
  803.   /* print nodes tables */
  804.   int i, j;
  805.   long numnodes, numlists, maxlistlen, listlen;
  806.   struct s1 *nd1;
  807.   struct s2 *nd2;
  808.  
  809.   maxlistlen = 0;
  810.   numnodes = 0;
  811.   numlists = 0;
  812.   for (i = 0; i < PRIME1; i++) {
  813.     if (a1[i]) {
  814.       fprintf (stderr, "a1[%d]\n", i);
  815.       numlists++;
  816.       listlen = 0;
  817.       for (j=1, nd1 = a1[i];  nd1 != NULL;  j++, nd1 = nd1->nextnode) {
  818.     fprintf (stderr, "%d) %d %ld %ld pa=%s, s=%s, po=%s t=%s\n",
  819.          j, nd1->lastused, nd1->nodeid, nd1,
  820.          nd1->gopherpath, nd1->gopherserver, nd1->gopherport,
  821.          nd1->gophertitle);
  822.     listlen++;
  823.       }
  824.       if (listlen > maxlistlen)
  825.     maxlistlen = listlen;
  826.     }
  827.   }
  828.  
  829.   fprintf (stderr, "\nNumlists = %d, maxlistlen = %d\n\n", numlists,maxlistlen);
  830.  
  831.   for (i = 0; i < PRIME2; i++) {
  832.     if (a2[i]) {
  833.       fprintf (stderr, "a2[%d].  ", i);
  834.       for (j=1, nd2 = a2[i];  nd2 != NULL;  j++, nd2 = nd2->nextnode) {
  835.     fprintf (stderr, "%d) %ld %ld %ld\n",
  836.          j, nd2->nodeid, nd2, nd2->node);
  837.       }
  838.     }
  839.   }
  840. }
  841.  
  842.